package com.ray.depot.service.impl;

import java.lang.reflect.InvocationTargetException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.ray.api.service.ActivitiApiService;
import com.ray.common.util.RedisLockUtil;
import com.ray.common.util.RedisNoUtil;
import com.ray.depot.enums.InOutStatus;
import com.ray.depot.enums.RecordType;
import com.ray.depot.service.InOutRecordService;
import com.ray.depot.service.InOutSerializeService;
import com.ray.depot.service.InOutService;
import com.ray.depot.service.MaterialLibraryService;
import com.ray.depot.service.StockSerializeService;
import com.ray.depot.service.StockService;
import com.ray.depot.table.entity.InOut;
import com.ray.depot.table.entity.InOutRecord;
import com.ray.depot.table.entity.InOutRecordExample;
import com.ray.depot.table.entity.InOutRecordExample.Criteria;
import com.ray.depot.table.entity.InOutSerialize;
import com.ray.depot.table.entity.MaterialLibrary;
import com.ray.depot.table.entity.Stock;
import com.ray.depot.table.entity.StockSerialize;
import com.ray.depot.table.mapper.InOutRecordMapper;
import com.ray.depot.table.vo.RecordVo;
import com.xiaoleilu.hutool.util.ObjectUtil;
import com.xiaoleilu.hutool.util.StrUtil;
import com.zjhc.flyhong.common.function.enums.DeleteEnum;
import com.zjhc.flyhong.common.function.exception.BussinessException;
import com.zjhc.flyhong.common.function.operator.LoginUser;
import com.zjhc.flyhong.common.function.operator.PageDto;
import com.zjhc.flyhong.common.function.validate.EntityValidate;
import com.zjhc.flyhong.common.function.validate.rule.FieldRule;
import com.zjhc.flyhong.common.function.validate.rule.RuleEnum;
import com.zjhc.flyhong.common.util.UUIDUtil;
import com.zjhc.flyhong.common.web.ErrorMsg;
import com.zjhc.flyhong.common.web.Result;
import com.zjhc.flyhong.common.web.SuccessMsg;
import com.zjhc.flyhong.common.web.base.BaseService;
import com.zjhc.flyhong.redis.DefaultRedisService;

@Service
public class InOutRecordServiceImpl extends BaseService implements InOutRecordService {
	/** 日志对象 **/
	private static Logger logger = Logger.getLogger(InOutRecordServiceImpl.class);

	@Autowired
	private InOutRecordMapper inOutRecordMapper;
	@Autowired
	private InOutSerializeService inOutSerializeService;
	@Autowired
	private InOutService inOutService;
	@Autowired
	private DefaultRedisService defaultRedisService;
	@Autowired
	private ActivitiApiService activitiApiService;
	@Autowired
	private MaterialLibraryService materialLibraryService;
	@Autowired
	private StockService stockService;
	@Autowired
	private StockSerializeService stockSerializeService;

	@Transactional
	public Result<Object> addInOutRecord(InOutRecord params, LoginUser user) {
		Result<Object> result = null;
		result = new Result<Object>(0, SuccessMsg.OP_SUCCESS);
		params.setTenantId(user.getTenantId());
		params.setId(UUIDUtil.getUUID());
		params.setIsDelete(DeleteEnum.USE.getValue());
		this.setUserForAdd(params, user);
		int count = inOutRecordMapper.insertSelective(params);
		if (count != 1) {
			logger.error("InOutRecordServiceImpl=>addInOutRecord is error");
			throw new BussinessException(-1, ErrorMsg.INSERT_ERROR);
		}
		return result;
	}

	@Transactional(propagation = Propagation.NOT_SUPPORTED)
	public InOutRecord queryInOutRecord(String id, LoginUser user) {
		return inOutRecordMapper.selectByPrimaryKey(id);
	}

	@Transactional
	public Result<Object> updateInOutRecord(InOutRecord params, LoginUser user) {
		Result<Object> result = null;
		result = new Result<Object>(0, SuccessMsg.OP_SUCCESS);
		this.setUserForUpdate(params, user);
		int count = inOutRecordMapper.updateByPrimaryKeySelective(params);
		if (count != 1) {
			logger.error("InOutRecordServiceImpl=>editInOutRecord is error");
			throw new BussinessException(-1, ErrorMsg.INSERT_ERROR);
		}
		return result;
	}

	@Transactional
	public Result<Object> deleteInOutRecord(String id, LoginUser user) {
		Result<Object> result = null;
		result = new Result<Object>(0, SuccessMsg.OP_SUCCESS);
		InOutRecord params = new InOutRecord();
		params.setId(id);
		params.setIsDelete(DeleteEnum.DELETE.getValue());
		this.setUserForUpdate(params, user);
		int count = inOutRecordMapper.updateByPrimaryKeySelective(params);
		if (count != 1) {
			logger.error("InOutRecordServiceImpl=>deleteInOutRecord is error");
			throw new BussinessException(-1, ErrorMsg.INSERT_ERROR);
		}
		return result;
	}

	@Transactional(propagation = Propagation.NOT_SUPPORTED)
	public PageDto<InOutRecord> queryInOutRecord(InOutRecord params, PageDto<InOutRecord> page, LoginUser user) {
		InOutRecordExample example = new InOutRecordExample();
		Criteria criteria = example.createCriteria();
		// 超级管理员能查看全部
		if (!user.isSuperManager()) {
			criteria.andTenantIdEqualTo(user.getTenantId());
		} else if (user.isManager()) {
			criteria.andCreateByOrgEqualTo(user.getDeptCode());
		} else {
			criteria.andCreateByUserEqualTo(user.getUserId());
		}

		if (StrUtil.isNotBlank(params.getRecordType())) {
			criteria.andRecordTypeEqualTo(params.getRecordType());
		}

		criteria.andIsDeleteEqualTo(DeleteEnum.USE.getValue());
		this.setExamplePage(example, page);
		page.setTotal(inOutRecordMapper.countByExample(example));
		page.setRecords(inOutRecordMapper.selectByExample(example));
		return page;
	}

	@Override
	@Transactional
	public Result<Object> saveInRecord(RecordVo record, Boolean isDaft, LoginUser user) {
		record.setInOutStatus(InOutStatus.draft.getType());
		record.setRecordType(RecordType.IN.getType());
		// 草稿状态下不需要校验
		if (!isDaft) {
			// 对象验证
			FieldRule fieldRule = new FieldRule();
			fieldRule.addRule(RuleEnum.IS_NOT_NULL, String.class, "warchouseId", "仓库");
			fieldRule.addRule(RuleEnum.IS_NOT_NULL, String.class, "sourceId", "供应商");
			fieldRule.addRule(RuleEnum.IS_NOT_NULL, String.class, "inOutType", "出入库类型");
			Result<Object> result = EntityValidate.validate(record, fieldRule);
			if (!result.codeSuccess()) {
				return result;
			}

			// 校验是否为空
			if (record.getInOuts().isEmpty() && record.getInOutSerializes().isEmpty()) {
				return new Result<>(-1, "没有入库的物料或资产");
			}

			// 物料校验
			if (!record.getInOuts().isEmpty()) {
				for (InOut inOut : record.getInOuts()) {
					fieldRule = new FieldRule();
					fieldRule.addRule(RuleEnum.IS_NOT_NULL, Date.class, "productTime", "生产日期");
					fieldRule.addRule(RuleEnum.IS_NOT_NULL, Integer.class, "quantity", "数量");
					result = EntityValidate.validate(inOut, fieldRule);
					if (!result.codeSuccess()) {
						return result;
					}
				}
			}
			// 资产校验
			if (!record.getInOutSerializes().isEmpty()) {
				for (InOutSerialize inOutSerialize : record.getInOutSerializes()) {
					fieldRule = new FieldRule();
					fieldRule.addRule(RuleEnum.IS_NOT_NULL, Date.class, "productTime", "生产日期");
					fieldRule.addRule(RuleEnum.IS_NOT_NULL, String.class, "serializaNo", "资产号");
					result = EntityValidate.validate(inOutSerialize, fieldRule);
					if (!result.codeSuccess()) {
						return result;
					}
				}
			}

			record.setInOutStatus(InOutStatus.ToSubmit.getType());
		}

		String inOutNo = RedisNoUtil.createNo("RK", defaultRedisService);
		record.setInOutNo(inOutNo);
		// 插入头
		this.addInOutRecord(record, user);
		// 插入物料
		if (!record.getInOuts().isEmpty()) {
			for (InOut inOut : record.getInOuts()) {
				inOut.setInOutNo(inOutNo);
				inOut.setWarchouseId(record.getWarchouseId());
				inOutService.addInOut(inOut, user);
			}
		}
		// 插入资产
		if (!record.getInOutSerializes().isEmpty()) {
			for (InOutSerialize inOutSerialize : record.getInOutSerializes()) {
				inOutSerialize.setInOutNo(inOutNo);
				inOutSerialize.setWarchouseId(record.getWarchouseId());
				inOutSerializeService.addInOutSerialize(inOutSerialize, user);
			}
		}
		return new Result<>(0, SuccessMsg.OP_SUCCESS, record);
	}

	@Override
	@Transactional
	public RecordVo saveInRecord(RecordVo record, String status, LoginUser user) {
		record.setInOutStatus(status);
		record.setRecordType(RecordType.IN.getType());
		if(StrUtil.equals(status, InOutStatus.InOutSuccess.getType())){
			record.setInOutTime(new Date());
		}
		// 对象验证
		FieldRule fieldRule = new FieldRule();
		fieldRule.addRule(RuleEnum.IS_NOT_NULL, String.class, "warchouseId", "仓库");
		fieldRule.addRule(RuleEnum.IS_NOT_NULL, String.class, "sourceId", "供应商");
		fieldRule.addRule(RuleEnum.IS_NOT_NULL, String.class, "inOutType", "出入库类型");
		Result<Object> result = EntityValidate.validate(record, fieldRule);
		if (!result.codeSuccess()) {
			throw new BussinessException(-1, result.getMsg());
		}

		// 校验是否为空
		if (record.getInOuts().isEmpty() && record.getInOutSerializes().isEmpty()) {
			throw new BussinessException(-1, "没有入库的物料或资产");
		}

		// 物料校验
		if (!record.getInOuts().isEmpty()) {
			for (InOut inOut : record.getInOuts()) {
				fieldRule = new FieldRule();
				fieldRule.addRule(RuleEnum.IS_NOT_NULL, Integer.class, "quantity", "数量");
				result = EntityValidate.validate(inOut, fieldRule);
				if (!result.codeSuccess()) {
					throw new BussinessException(-1, result.getMsg());
				}
			}
		}
		// 资产校验
		if (!record.getInOutSerializes().isEmpty()) {
			for (InOutSerialize inOutSerialize : record.getInOutSerializes()) {
				fieldRule = new FieldRule();
				fieldRule.addRule(RuleEnum.IS_NOT_NULL, String.class, "serializaNo", "资产号");
				result = EntityValidate.validate(inOutSerialize, fieldRule);
				if (!result.codeSuccess()) {
					throw new BussinessException(-1, result.getMsg());
				}
			}
		}

		String inOutNo = RedisNoUtil.createNo("RK", defaultRedisService);
		record.setInOutNo(inOutNo);
		// 插入头
		this.addInOutRecord(record, user);
		// 插入物料
		if (!record.getInOuts().isEmpty()) {
			for (InOut inOut : record.getInOuts()) {
				inOut.setInOutNo(inOutNo);
				inOut.setWarchouseId(record.getWarchouseId());
				inOutService.addInOut(inOut, user);
			}
		}
		// 插入资产
		if (!record.getInOutSerializes().isEmpty()) {
			for (InOutSerialize inOutSerialize : record.getInOutSerializes()) {
				inOutSerialize.setInOutNo(inOutNo);
				inOutSerialize.setWarchouseId(record.getWarchouseId());
				inOutSerializeService.addInOutSerialize(inOutSerialize, user);
			}
		}
		return record;
	}

	@Override
	@Transactional
	public Result<Object> editInRecord(RecordVo record, LoginUser user) {
		InOutRecord inOutRecord = this.queryInOutRecord(record.getId(), user);
		// 判断自有在草稿 审核不同意 带提交的时候才能修改
		if (!InOutStatus.canEdit(inOutRecord.getInOutStatus())) {
			return new Result<>(-1, "该状态不能编辑");
		}
		// 草稿状态下不需要校验
		// 对象验证
		FieldRule fieldRule = new FieldRule();
		fieldRule.addRule(RuleEnum.IS_NOT_NULL, String.class, "warchouseId", "仓库");
		fieldRule.addRule(RuleEnum.IS_NOT_NULL, String.class, "sourceId", "供应商");
		fieldRule.addRule(RuleEnum.IS_NOT_NULL, String.class, "inOutType", "出入库类型");
		fieldRule.addRule(RuleEnum.IS_NOT_NULL, String.class, "inOutNo", "出入库单号");
		Result<Object> result = EntityValidate.validate(record, fieldRule);
		if (!result.codeSuccess()) {
			return result;
		}
		// 校验是否为空
		if (record.getInOuts().isEmpty() && record.getInOutSerializes().isEmpty()) {
			return new Result<>(-1, "没有入库的物料或资产");
		}

		String inOutNo = record.getInOutNo();
		// 审批驳回状态 不用修改
		if (!StrUtil.equals(inOutRecord.getInOutStatus(), InOutStatus.ApplyFail.getType())) {
			record.setInOutStatus(InOutStatus.ToSubmit.getType());
		}
		// 插入头
		this.updateInOutRecord(record, user);
		// 插入物料
		// 先删除物料
		inOutService.deleteInOutByNo(inOutNo, user);
		if (!record.getInOuts().isEmpty()) {
			for (InOut inOut : record.getInOuts()) {
				inOut.setInOutNo(inOutNo);
				inOut.setWarchouseId(record.getWarchouseId());
				inOutService.addInOut(inOut, user);
			}
		}
		// 插入资产
		// 先删除资产
		inOutSerializeService.deleteInOutSerializeByNo(inOutNo, user);
		if (!record.getInOutSerializes().isEmpty()) {
			for (InOutSerialize inOutSerialize : record.getInOutSerializes()) {
				inOutSerialize.setInOutNo(inOutNo);
				inOutSerialize.setWarchouseId(record.getWarchouseId());
				inOutSerializeService.addInOutSerialize(inOutSerialize, user);
			}
		}

		return new Result<>(0, SuccessMsg.OP_SUCCESS);
	}

	@Override
	@Transactional
	public Result<Object> deleteInRecord(String id, LoginUser user) {
		InOutRecord inOutRecord = this.queryInOutRecord(id, user);
		if (!InOutStatus.canDelete(inOutRecord.getInOutStatus())) {
			return new Result<>(-1, "该状态不能删除");
		}
		String inOutNo = inOutRecord.getInOutNo();
		// 删除头
		this.deleteInOutRecord(id, user);
		// 先删除物料
		inOutService.deleteInOutByNo(inOutNo, user);
		// 先删除资产
		inOutSerializeService.deleteInOutSerializeByNo(inOutNo, user);
		return new Result<>(0, SuccessMsg.OP_SUCCESS);
	}

	@Override
	@Transactional
	public Result<Object> applyRecord(String inOutNo, String userids, String deployId, String defId, String actKey,
			LoginUser user) {
		InOutRecord inOutRecord = this.queryInOutRecordByInOutNo(inOutNo, user);
		if (!InOutStatus.canSubmitApply(inOutRecord.getInOutStatus())) {
			return new Result<>(-1, "该状态不能审核");
		}
		// 更新订单状态
		// updateStatusByInOutNo(inOutNo,InOutStatus.ToApply.getType(),user);
		try {
			activitiApiService.startFlow(inOutNo, actKey, userids, deployId, defId, user);
		} catch (Exception e) {
			throw new BussinessException(-1, "流程启动异常");
		}
		return new Result<>(0, SuccessMsg.OP_SUCCESS);
	}

	/**
	 * 查询
	 */
	public InOutRecord queryInOutRecordByInOutNo(String inOutNo, LoginUser user) {
		InOutRecordExample example = new InOutRecordExample();
		example.createCriteria().andInOutNoEqualTo(inOutNo).andIsDeleteEqualTo(DeleteEnum.USE.getValue());
		List<InOutRecord> lists = inOutRecordMapper.selectByExample(example);
		if (lists == null || lists.isEmpty()) {
			throw new BussinessException(-1, "不存在该记录");
		}
		if (lists.size() > 1) {
			throw new BussinessException(-1, "查询到多条该记录");
		}
		return lists.get(0);
	}

	@Override
	@Transactional
	public void applyFail(String inOutNo) {
		// 更新订单状态
		updateStatusByInOutNo(inOutNo, InOutStatus.ApplyFail.getType(), null);
	}

	@Override
	@Transactional
	public void applySuccess(String inOutNo) {
		// 更新订单状态
		updateStatusByInOutNo(inOutNo, InOutStatus.ToInOut.getType(), null);
	}

	@Override
	@Transactional
	public void applyIng(String inOutNo) {
		// 更新订单状态
		updateStatusByInOutNo(inOutNo, InOutStatus.ToApply.getType(), null);
	}

	@Override
	@Transactional
	public void applyCancel(String inOutNo) {
		// 更新订单状态
		updateStatusByInOutNo(inOutNo, InOutStatus.ApplyCancel.getType(), null);
	}

	@Override
	@Transactional
	public Result<Object> storeIn(String id, LoginUser user) {
		// 校验物料在仓库中有没有库位
		// 入库单头
		InOutRecord record = this.queryInOutRecord(id, user);
		if (ObjectUtil.isNull(record)) {
			return new Result<>(-1, String.format("无法找到该出入库单:%s", id));
		}
		if (StrUtil.isBlank(record.getWarchouseId())) {
			return new Result<>(-1, String.format("没有入库仓库信息:%s", record.getWarchouseId()));
		}
		if (!InOutStatus.canInOut(record.getInOutStatus())) {
			return new Result<>(-1, "该状态不能入库");
		}
		// 校验是否已经在入库
		if (RedisLockUtil.isExitKey(record.getInOutNo(), defaultRedisService)) {
			return new Result<>(-1, "入库单正在出入库,请不要重复操作.");
		}
		try {
			// 入库物料
			List<InOut> inOuts = inOutService.queryInOutsByInOutNo(record.getInOutNo(), user);
			// 物料MAP
			Map<String, InOut> inOutMap = new HashMap<>();
			// 校验是否存在库位
			MaterialLibrary library = null;
			for (InOut inOut : inOuts) {
				if (inOut.getQuantity() == null || inOut.getQuantity() <= 0) {
					return new Result<>(-1, String.format("物料:%s入库数量错误", inOut.getMaterialId()));
				}
				library = materialLibraryService.selectLibrary(inOut.getMaterialId(), record.getWarchouseId());
				if (ObjectUtil.isNull(library)) {
					return new Result<>(-1,
							String.format("仓库编号：%s的仓库中没有物料:%s存在的位置", record.getWarchouseId(), inOut.getMaterialId()));
				}
				// 存放库位
				inOut.setLibraryId(library.getLibraryId());
				inOut.setWarchouseId(record.getWarchouseId());
				// 存在数据
				inOutMap.put(inOut.getMaterialId(), inOut);
			}
			// 入库资产
			List<InOutSerialize> inOutSerializes = inOutSerializeService
					.queryInOutSerializesByInOutNo(record.getInOutNo(), user);
			// 校验是否存在库位
			for (InOutSerialize inOutSerialize : inOutSerializes) {
				if (inOutSerialize.getQuantity() == null || inOutSerialize.getQuantity() <= 0) {
					return new Result<>(-1, String.format("物料:%s入库数量错误", inOutSerialize.getMaterialId()));
				}
				library = materialLibraryService.selectLibrary(inOutSerialize.getMaterialId(), record.getWarchouseId());

				if (ObjectUtil.isNull(library)) {
					return new Result<>(-1, String.format("仓库编号：%s的仓库中没有物料:%s存在的位置", record.getWarchouseId(),
							inOutSerialize.getMaterialId()));
				}
				// 存放库位
				inOutSerialize.setLibraryId(library.getLibraryId());
				inOutSerialize.setWarchouseId(record.getWarchouseId());

				InOut newInout = inOutMap.get(inOutSerialize.getMaterialId());
				if (ObjectUtil.isNull(newInout)) {
					newInout = new InOut();
					try {
						PropertyUtils.copyProperties(newInout, inOutSerialize);
					} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
						throw new BussinessException(-1, "数据校验复制异常");
					}
					newInout.setQuantity(0);
					// 放入列表
					inOuts.add(newInout);
					inOutMap.put(inOutSerialize.getMaterialId(), newInout);
				}
				// 新增数量
				newInout.setQuantity(newInout.getQuantity() + inOutSerialize.getQuantity());
			}

			// 入库整合 如果物料 和资产的ID一样 需要把资产的数量叠加到 物料上
			// 入库操作
			stockService.storeIn(record, inOuts, inOutSerializes, user);
			// 更新状态
			updateStatusByInOutNoWtihStoreIn(record.getInOutNo(), InOutStatus.InOutSuccess.getType(), user);
		} finally {
			// 最后释放
			RedisLockUtil.deleteKey(record.getInOutNo(), defaultRedisService);
		}
		// 更新状态
		return new Result<>(0, SuccessMsg.OP_SUCCESS);
	}

	@Transactional
	public void storeIn(RecordVo record, LoginUser user) {
		// 校验物料在仓库中有没有库位
		// 入库单头
		if (ObjectUtil.isNull(record)) {
			throw new BussinessException(-1, String.format("无法找到该出入库单:%s", record.getInOutNo()));
		}
		if (StrUtil.isBlank(record.getWarchouseId())) {
			throw new BussinessException(-1, String.format("没有入库仓库信息:%s", record.getWarchouseId()));
		}
		// 入库物料
		List<InOut> inOuts = record.getInOuts();
		// 物料MAP
		Map<String, InOut> inOutMap = new HashMap<>();
		// 校验是否存在库位
		MaterialLibrary library = null;
		for (InOut inOut : inOuts) {
			if (inOut.getQuantity() == null || inOut.getQuantity() <= 0) {
				throw new BussinessException(-1, String.format("物料:%s入库数量错误", inOut.getMaterialId()));
			}
			library = materialLibraryService.selectLibrary(inOut.getMaterialId(), record.getWarchouseId());
			if (ObjectUtil.isNull(library)) {
				throw new BussinessException(-1,
						String.format("仓库编号：%s的仓库中没有物料:%s存在的位置", record.getWarchouseId(), inOut.getMaterialId()));
			}
			// 存放库位
			inOut.setLibraryId(library.getLibraryId());
			inOut.setWarchouseId(record.getWarchouseId());
			// 存在数据
			inOutMap.put(inOut.getMaterialId(), inOut);
		}
		// 入库资产
		List<InOutSerialize> inOutSerializes = record.getInOutSerializes();
		// 校验是否存在库位
		for (InOutSerialize inOutSerialize : inOutSerializes) {
			if (inOutSerialize.getQuantity() == null || inOutSerialize.getQuantity() <= 0) {
				throw new BussinessException(-1, String.format("物料:%s入库数量错误", inOutSerialize.getMaterialId()));
			}
			library = materialLibraryService.selectLibrary(inOutSerialize.getMaterialId(), record.getWarchouseId());

			if (ObjectUtil.isNull(library)) {
				throw new BussinessException(-1, String.format("仓库编号：%s的仓库中没有物料:%s存在的位置", record.getWarchouseId(),
						inOutSerialize.getMaterialId()));
			}
			// 存放库位
			inOutSerialize.setLibraryId(library.getLibraryId());
			inOutSerialize.setWarchouseId(record.getWarchouseId());

			InOut newInout = inOutMap.get(inOutSerialize.getMaterialId());
			if (ObjectUtil.isNull(newInout)) {
				newInout = new InOut();
				try {
					PropertyUtils.copyProperties(newInout, inOutSerialize);
				} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
					throw new BussinessException(-1, "数据校验复制异常");
				}
				newInout.setQuantity(0);
				// 放入列表
				inOuts.add(newInout);
				inOutMap.put(inOutSerialize.getMaterialId(), newInout);
			}
			// 新增数量
			newInout.setQuantity(newInout.getQuantity() + inOutSerialize.getQuantity());
		}

		// 入库整合 如果物料 和资产的ID一样 需要把资产的数量叠加到 物料上
		// 入库操作
		stockService.storeIn(record, inOuts, inOutSerializes, user);

	}

	

	@Override
	@Transactional
	public Result<Object> editOutRecord(RecordVo record, LoginUser user) {
		InOutRecord inOutRecord = this.queryInOutRecord(record.getId(), user);
		// 判断自有在草稿 审核不同意 带提交的时候才能修改
		if (!InOutStatus.canEdit(inOutRecord.getInOutStatus())) {
			return new Result<>(-1, "该状态不能编辑");
		}
		// 草稿状态下不需要校验
		// 对象验证
		FieldRule fieldRule = new FieldRule();
		fieldRule.addRule(RuleEnum.IS_NOT_NULL, String.class, "warchouseId", "仓库");
		fieldRule.addRule(RuleEnum.IS_NOT_NULL, String.class, "sourceId", "供应商");
		fieldRule.addRule(RuleEnum.IS_NOT_NULL, String.class, "inOutType", "出入库类型");
		fieldRule.addRule(RuleEnum.IS_NOT_NULL, String.class, "inOutNo", "出入库单号");
		Result<Object> result = EntityValidate.validate(record, fieldRule);
		if (!result.codeSuccess()) {
			return result;
		}
		// 校验是否为空
		if (record.getInOuts().isEmpty() && record.getInOutSerializes().isEmpty()) {
			return new Result<>(-1, "没有入库的物料或资产");
		}

		String inOutNo = record.getInOutNo();
		// 审批驳回状态 不用修改
		if (!StrUtil.equals(inOutRecord.getInOutStatus(), InOutStatus.ApplyFail.getType())) {
			record.setInOutStatus(InOutStatus.ToSubmit.getType());
		}
		// 插入头
		this.updateInOutRecord(record, user);
		// 插入物料
		// 先删除物料
		inOutService.deleteInOutByNo(inOutNo, user);
		if (!record.getInOuts().isEmpty()) {
			for (InOut inOut : record.getInOuts()) {
				inOut.setInOutNo(inOutNo);
				inOut.setWarchouseId(record.getWarchouseId());
				inOutService.addInOut(inOut, user);
			}
		}
		// 插入资产
		// 先删除资产
		inOutSerializeService.deleteInOutSerializeByNo(inOutNo, user);
		if (!record.getInOutSerializes().isEmpty()) {
			for (InOutSerialize inOutSerialize : record.getInOutSerializes()) {
				inOutSerialize.setInOutNo(inOutNo);
				inOutSerialize.setWarchouseId(record.getWarchouseId());
				inOutSerializeService.addInOutSerialize(inOutSerialize, user);
			}
		}

		return new Result<>(0, SuccessMsg.OP_SUCCESS);
	}

	@Override
	@Transactional
	public Result<Object> storeOut(String id, LoginUser user) {
		// 校验物料在仓库中有没有库位
		// 出库单头
		InOutRecord record = this.queryInOutRecord(id, user);
		if (ObjectUtil.isNull(record)) {
			return new Result<>(-1, String.format("无法找到该出库单:%s", id));
		}
		if (StrUtil.isBlank(record.getWarchouseId())) {
			return new Result<>(-1, String.format("没有出库库仓库信息:%s", record.getWarchouseId()));
		}
		if (!InOutStatus.canInOut(record.getInOutStatus())) {
			return new Result<>(-1, "该状态不能出库");
		}
		// 校验是否已经在出库
		if (RedisLockUtil.isExitKey(record.getInOutNo(), defaultRedisService)) {
			return new Result<>(-1, "出库单正在出库,请不要重复操作.");
		}
       try{
			// 出库库物料
			List<InOut> inOuts = inOutService.queryInOutsByInOutNo(record.getInOutNo(), user);
			// 物料MAP
			Map<String, InOut> inOutMap = new HashMap<>();
			// 校验是否存在库位
			Stock stock = null;
			StockSerialize stockSerialize = null;
			for (InOut inOut : inOuts) {
				inOut.setWarchouseId(record.getWarchouseId());
				// 存在数据
				inOutMap.put(inOut.getMaterialId(), inOut);
			}
			// 出库资产
			List<InOutSerialize> inOutSerializes = inOutSerializeService.queryInOutSerializesByInOutNo(record.getInOutNo(),
					user);
			// 校验是否存在库位
			for (InOutSerialize inOutSerialize : inOutSerializes) {
				if (inOutSerialize.getQuantity() == null || inOutSerialize.getQuantity() <= 0) {
					return new Result<>(-1, String.format("物料:%s入库数量错误", inOutSerialize.getMaterialId()));
				}
				// 查询库存
				stockSerialize = stockSerializeService.queryStockSerialize(inOutSerialize.getMaterialId(),
						inOutSerialize.getSerializaNo(), record.getWarchouseId());
				if (ObjectUtil.isNull(stockSerialize)) {
					return new Result<>(-1,
							String.format("仓库编号：%s的仓库中物料:%s库存不足", record.getWarchouseId(), inOutSerialize.getMaterialId()));
				}
				// 出库库位
				inOutSerialize.setWarchouseId(record.getWarchouseId());
	
				InOut newInout = inOutMap.get(inOutSerialize.getMaterialId());
				if (ObjectUtil.isNull(newInout)) {
					newInout = new InOut();
					try {
						PropertyUtils.copyProperties(newInout, inOutSerialize);
					} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
						throw new BussinessException(-1, "数据校验复制异常");
					}
					newInout.setQuantity(0);
					// 放入列表
					inOuts.add(newInout);
					inOutMap.put(inOutSerialize.getMaterialId(), newInout);
				}
				// 新增数量
				newInout.setQuantity(newInout.getQuantity() + inOutSerialize.getQuantity());
			}
	
			// 数据校验
			for (InOut inOut : inOuts) {
				if (inOut.getQuantity() == null || inOut.getQuantity() <= 0) {
					return new Result<>(-1, String.format("物料:%s入库数量错误", inOut.getMaterialId()));
				}
				// 查询库存
				stock = stockService.selectStock(inOut.getMaterialId(), record.getWarchouseId());
				if (ObjectUtil.isNull(stock) || inOut.getQuantity() > stock.getQuantity()) {
					return new Result<>(-1,
							String.format("仓库编号：%s的仓库中物料:%s库存不足", record.getWarchouseId(), inOut.getMaterialId()));
				}
			}
			// 出库整合 如果物料 和资产的ID一样 需要把资产的数量叠加到 物料上
			// 出库操作
			stockService.storeOut(record, inOuts, inOutSerializes, user);
			// 更新状态
			updateStatusByInOutNoWtihStoreIn(record.getInOutNo(), InOutStatus.InOutSuccess.getType(), user);
        }finally {
			// 最后释放
			RedisLockUtil.deleteKey(record.getInOutNo(), defaultRedisService);
		}
		return new Result<>(0, SuccessMsg.OP_SUCCESS);
	}

	/**
	 * 创建出库单 
	 * @param record
	 * @param isDaft
	 * @param user
	 * @return
	 */
	@Transactional
	public Result<Object> saveOutRecord(RecordVo record, Boolean isDaft, LoginUser user) {
		record.setInOutStatus(InOutStatus.draft.getType());
		record.setRecordType(RecordType.OUT.getType());
		// 草稿状态下不需要校验
		if (!isDaft) {
			// 对象验证
			FieldRule fieldRule = new FieldRule();
			fieldRule.addRule(RuleEnum.IS_NOT_NULL, String.class, "warchouseId", "仓库");
			fieldRule.addRule(RuleEnum.IS_NOT_NULL, String.class, "sourceId", "客户");
			fieldRule.addRule(RuleEnum.IS_NOT_NULL, String.class, "inOutType", "出入库类型");
			Result<Object> result = EntityValidate.validate(record, fieldRule);
			if (!result.codeSuccess()) {
				return result;
			}

			// 校验是否为空
			if (record.getInOuts().isEmpty() && record.getInOutSerializes().isEmpty()) {
				return new Result<>(-1, "没有出库的物料或资产");
			}

			// 物料校验
			if (!record.getInOuts().isEmpty()) {
				for (InOut inOut : record.getInOuts()) {
					fieldRule = new FieldRule();
					fieldRule.addRule(RuleEnum.IS_NOT_NULL, Integer.class, "quantity", "数量");
					result = EntityValidate.validate(inOut, fieldRule);
					if (!result.codeSuccess()) {
						return result;
					}
				}
			}
			// 资产校验
			if (!record.getInOutSerializes().isEmpty()) {
				for (InOutSerialize inOutSerialize : record.getInOutSerializes()) {
					fieldRule = new FieldRule();
					fieldRule.addRule(RuleEnum.IS_NOT_NULL, String.class, "serializaNo", "资产号");
					result = EntityValidate.validate(inOutSerialize, fieldRule);
					if (!result.codeSuccess()) {
						return result;
					}
				}
			}

			record.setInOutStatus(InOutStatus.ToSubmit.getType());
		}

		String inOutNo = RedisNoUtil.createNo("CK", defaultRedisService);
		record.setInOutNo(inOutNo);
		// 插入头
		this.addInOutRecord(record, user);
		// 插入物料
		if (!record.getInOuts().isEmpty()) {
			for (InOut inOut : record.getInOuts()) {
				inOut.setInOutNo(inOutNo);
				inOut.setWarchouseId(record.getWarchouseId());
				inOutService.addInOut(inOut, user);
			}
		}
		// 插入资产
		if (!record.getInOutSerializes().isEmpty()) {
			for (InOutSerialize inOutSerialize : record.getInOutSerializes()) {
				inOutSerialize.setInOutNo(inOutNo);
				inOutSerialize.setWarchouseId(record.getWarchouseId());
				inOutSerializeService.addInOutSerialize(inOutSerialize, user);
			}
		}
		return new Result<>(0, SuccessMsg.OP_SUCCESS, record);
	}
	
	@Transactional
	public void storeOut(RecordVo record, LoginUser user) {
		// 校验物料在仓库中有没有库位
		// 出库单头
		if (ObjectUtil.isNull(record)) {
			throw new BussinessException(-1, String.format("无法找到该出库单:%s", record.getInOutNo()));
		}
		if (StrUtil.isBlank(record.getWarchouseId())) {
			throw new BussinessException(-1, String.format("没有出库库仓库信息:%s", record.getWarchouseId()));
		}

		// 出库库物料
		List<InOut> inOuts = record.getInOuts();
		// 物料MAP
		Map<String, InOut> inOutMap = new HashMap<>();
		// 校验是否存在库位
		Stock stock = null;
		StockSerialize stockSerialize = null;
		for (InOut inOut : inOuts) {
			inOut.setWarchouseId(record.getWarchouseId());
			// 存在数据
			inOutMap.put(inOut.getMaterialId(), inOut);
		}
		// 出库资产
		List<InOutSerialize> inOutSerializes = record.getInOutSerializes();
		// 校验是否存在库位
		for (InOutSerialize inOutSerialize : inOutSerializes) {
			if (inOutSerialize.getQuantity() == null || inOutSerialize.getQuantity() <= 0) {
				throw new BussinessException(-1, String.format("物料:%s入库数量错误", inOutSerialize.getMaterialId()));
			}
			// 查询库存
			stockSerialize = stockSerializeService.queryStockSerialize(inOutSerialize.getMaterialId(),
					inOutSerialize.getSerializaNo(), record.getWarchouseId());
			if (ObjectUtil.isNull(stockSerialize)) {
				throw new BussinessException(-1,
						String.format("仓库编号：%s的仓库中物料:%s库存不足", record.getWarchouseId(), inOutSerialize.getMaterialId()));
			}
			// 出库库位
			inOutSerialize.setWarchouseId(record.getWarchouseId());

			InOut newInout = inOutMap.get(inOutSerialize.getMaterialId());
			if (ObjectUtil.isNull(newInout)) {
				newInout = new InOut();
				try {
					PropertyUtils.copyProperties(newInout, inOutSerialize);
				} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
					throw new BussinessException(-1, "数据校验复制异常");
				}
				newInout.setQuantity(0);
				// 放入列表
				inOuts.add(newInout);
				inOutMap.put(inOutSerialize.getMaterialId(), newInout);
			}
			// 新增数量
			newInout.setQuantity(newInout.getQuantity() + inOutSerialize.getQuantity());
		}

		// 数据校验
		for (InOut inOut : inOuts) {
			if (inOut.getQuantity() == null || inOut.getQuantity() <= 0) {
				throw new BussinessException(-1, String.format("物料:%s入库数量错误", inOut.getMaterialId()));
			}
			// 查询库存
			stock = stockService.selectStock(inOut.getMaterialId(), record.getWarchouseId());
			if (ObjectUtil.isNull(stock) || inOut.getQuantity() > stock.getQuantity()) {
				throw new BussinessException(-1,
						String.format("仓库编号：%s的仓库中物料:%s库存不足", record.getWarchouseId(), inOut.getMaterialId()));
			}
		}
		// 出库整合 如果物料 和资产的ID一样 需要把资产的数量叠加到 物料上
		// 出库操作
		stockService.storeOut(record, inOuts, inOutSerializes, user);
	}

	@Override
	@Transactional
	public Result<Object> deleteOutRecord(String id, LoginUser user) {
		InOutRecord inOutRecord = this.queryInOutRecord(id, user);
		if (!InOutStatus.canDelete(inOutRecord.getInOutStatus())) {
			return new Result<>(-1, "该状态不能删除");
		}
		String inOutNo = inOutRecord.getInOutNo();
		// 删除头
		this.deleteInOutRecord(id, user);
		// 先删除物料
		inOutService.deleteInOutByNo(inOutNo, user);
		// 先删除资产
		inOutSerializeService.deleteInOutSerializeByNo(inOutNo, user);
		return new Result<>(0, SuccessMsg.OP_SUCCESS);
	}
	
	@Override
	@Transactional
	public RecordVo saveInRecordAndDoIn(RecordVo record, LoginUser user) {
		//生成入库单
		record = this.saveInRecord(record, InOutStatus.InOutSuccess.getType(), user);
		//入库操作
		this.storeIn(record, user);
		return record;
	}

	@Override
	@Transactional
	public RecordVo saveOutRecordAndDoOut(RecordVo record, LoginUser user) {
		//生成出库单
		record = this.saveOutRecord(record, InOutStatus.InOutSuccess.getType(), user);
		//出库操作
		this.storeOut(record, user);
		return record;
	}
	
	  /**
     * 创建入库单
     * @param record
     * @param status
     * @param user
     * @return
     */
	@Transactional
	public RecordVo saveOutRecord(RecordVo record, String status, LoginUser user) {
		record.setInOutStatus(status);
		record.setRecordType(RecordType.OUT.getType());
		if(StrUtil.equals(status, InOutStatus.InOutSuccess.getType())){
			record.setInOutTime(new Date());
		}
		// 对象验证
		FieldRule fieldRule = new FieldRule();
		fieldRule.addRule(RuleEnum.IS_NOT_NULL, String.class, "warchouseId", "仓库");
		fieldRule.addRule(RuleEnum.IS_NOT_NULL, String.class, "sourceId", "客户");
		fieldRule.addRule(RuleEnum.IS_NOT_NULL, String.class, "inOutType", "出入库类型");
		Result<Object> result = EntityValidate.validate(record, fieldRule);
		if (!result.codeSuccess()) {
			throw new BussinessException(-1, result.getMsg());
		}

		// 校验是否为空
		if (record.getInOuts().isEmpty() && record.getInOutSerializes().isEmpty()) {
			throw new BussinessException(-1, "没有出库的物料或资产");
		}

		// 物料校验
		if (!record.getInOuts().isEmpty()) {
			for (InOut inOut : record.getInOuts()) {
				fieldRule = new FieldRule();
				fieldRule.addRule(RuleEnum.IS_NOT_NULL, Integer.class, "quantity", "数量");
				result = EntityValidate.validate(inOut, fieldRule);
				if (!result.codeSuccess()) {
					throw new BussinessException(-1, result.getMsg());
				}
			}
		}
		// 资产校验
		if (!record.getInOutSerializes().isEmpty()) {
			for (InOutSerialize inOutSerialize : record.getInOutSerializes()) {
				fieldRule = new FieldRule();
				fieldRule.addRule(RuleEnum.IS_NOT_NULL, String.class, "serializaNo", "资产号");
				result = EntityValidate.validate(inOutSerialize, fieldRule);
				if (!result.codeSuccess()) {
					throw new BussinessException(-1, result.getMsg());
				}
			}
		}

		String inOutNo = RedisNoUtil.createNo("CK", defaultRedisService);
		record.setInOutNo(inOutNo);
		// 插入头
		this.addInOutRecord(record, user);
		// 插入物料
		if (!record.getInOuts().isEmpty()) {
			for (InOut inOut : record.getInOuts()) {
				inOut.setInOutNo(inOutNo);
				inOut.setWarchouseId(record.getWarchouseId());
				inOutService.addInOut(inOut, user);
			}
		}
		// 插入资产
		if (!record.getInOutSerializes().isEmpty()) {
			for (InOutSerialize inOutSerialize : record.getInOutSerializes()) {
				inOutSerialize.setInOutNo(inOutNo);
				inOutSerialize.setWarchouseId(record.getWarchouseId());
				inOutSerializeService.addInOutSerialize(inOutSerialize, user);
			}
		}
		return record;
	}


	/*********** 内部方法 ***********************/
	@Transactional
	private void updateStatusByInOutNo(String inOutNo, String status, LoginUser user) {
		InOutRecordExample example = new InOutRecordExample();
		example.createCriteria().andInOutNoEqualTo(inOutNo);
		InOutRecord entity = new InOutRecord();
		entity.setInOutStatus(status);
		// this.setUserForUpdate(entity, user);
		inOutRecordMapper.updateByExampleSelective(entity, example);
	}

	@Transactional
	private void updateStatusByInOutNoWtihStoreIn(String inOutNo, String status, LoginUser user) {
		InOutRecordExample example = new InOutRecordExample();
		example.createCriteria().andInOutNoEqualTo(inOutNo);
		InOutRecord entity = new InOutRecord();
		entity.setInOutStatus(status);
		entity.setInOutTime(new Date());
		entity.setOpUser(user.getUserId());
		this.setUserForUpdate(entity, user);
		inOutRecordMapper.updateByExampleSelective(entity, example);
	}
	
  
	

}
